/**
 * 增强版Excel公式解析器，支持循环函数和公共变量
 */
class ExcelFormulaParser {
  /**
   * 构造函数
   * @param {Object} publicVars - 公共变量映射 {变量名: 值}
   */
  constructor(publicVars = {}) {
    this.publicVars = publicVars
    this.functions = {
      // 逻辑函数
      IF: this._parseIf,
      AND: this._parseAnd,
      OR: this._parseOr,
      NOT: this._parseNot,
      XOR: this._parseXor,

      // 比较函数
      EQ: this._parseEq,
      NE: this._parseNe,
      GT: this._parseGt,
      GE: this._parseGe,
      LT: this._parseLt,
      LE: this._parseLe,

      // 数学函数
      ADD: this._parseAdd,
      SUB: this._parseSub,
      MUL: this._parseMul,
      DIV: this._parseDiv,
      MOD: this._parseMod,
      POW: this._parsePow,

      // 循环函数
      SUM: this._parseSum,
      PRODUCT: this._parseProduct,
      COUNT: this._parseCount,
      AVERAGE: this._parseAverage,
      MIN: this._parseMin,
      MAX: this._parseMax,

      // 日期函数
      DATE: this._parseDate,
      DATEDIFF: this._parseDateDiff,
      DAY: this._parseDay,
      MONTH: this._parseMonth,
      YEAR: this._parseYear,

      // 文本函数
      CONCAT: this._parseConcat,
      LEN: this._parseLen,
      LEFT: this._parseLeft,
      RIGHT: this._parseRight,

      // 数组函数
      MAP: this._parseMap,
      FILTER: this._parseFilter,
      REDUCE: this._parseReduce,
      ARRAY: this._parseArray
    }
  }

  /**
   * 解析Excel公式（直接返回计算结果）
   * @param {string} formula - Excel风格公式
   * @returns {*} 计算结果
   */
  parse(formula) {
    // 替换字段名为变量访问
    let jsCode = formula
    const fieldPattern = /{([^{}]+)}/g
    let match

    while ((match = fieldPattern.exec(formula)) !== null) {
      const fieldName = match[1]
      // 检查字段是否在公共变量中定义
      if (!(fieldName in this.publicVars)) {
        throw new Error(`未定义变量: ${fieldName}`)
      }
      jsCode = jsCode.replace(new RegExp(`{${fieldName}}`, 'g'), `this.publicVars['${fieldName}']`)
    }

    // 解析Excel函数
    jsCode = this._parseFunctions(jsCode)

    console.log('jsCode :>> ', jsCode)
    // console.log('this.publicVars :>> ', this.publicVars)

    // 创建执行函数并返回结果
    try {
      const func = new Function(`
        "use strict";
        return ${jsCode};
      `).bind({ publicVars: this.publicVars })

      return func()
    } catch (error) {
      throw new Error(`公式执行错误: ${error.message}\n公式: ${formula}`)
    }
  }

  /**
   * 添加或更新公共变量
   * @param {string} name - 变量名
   * @param {*} value - 变量值
   */
  setPublicVar(name, value) {
    this.publicVars[name] = value
  }

  /**
   * 批量设置公共变量
   * @param {Object} vars - 变量对象 {变量名: 值}
   */
  setPublicVars(vars) {
    this.publicVars = { ...this.publicVars, ...vars }
  }

  // 私有方法 =======================================================

  /**
   * 提取公式中的字段名
   * @private
   */
  _extractFields(formula) {
    const fields = new Set()
    const pattern = /{([^{}]+)}/g
    let match
    while ((match = pattern.exec(formula)) !== null) {
      fields.add(match[1])
    }
    return Array.from(fields)
  }

  /**
   * 递归解析函数调用（支持多层嵌套）
   * @private
   */
  _parseFunctions(expression) {
    // 按函数名长度降序排序（避免短名前缀误匹配）
    const funcNames = Object.keys(this.functions).sort((a, b) => b.length - a.length)
    let output = ''
    let index = 0
    const n = expression.length

    while (index < n) {
      let found = false

      // 尝试匹配所有已知函数
      for (const funcName of funcNames) {
        if (expression.startsWith(funcName, index)) {
          // 检查函数名边界（前无字母数字，后接括号）
          const prevChar = index > 0 ? expression[index - 1] : ''
          const nextCharIndex = index + funcName.length
          const nextChar = nextCharIndex < n ? expression[nextCharIndex] : ''

          if (
            !/[A-Za-z0-9_]/.test(prevChar) && // 确保不是子字符串
            (nextChar === '(' || /[\s]/.test(nextChar)) // 允许函数名后接空格
          ) {
            // 跳过函数名后的空格
            let parenIndex = nextCharIndex
            while (parenIndex < n && expression[parenIndex] === ' ') {
              parenIndex++
            }

            if (parenIndex < n && expression[parenIndex] === '(') {
              found = true
              // 提取函数参数部分
              const [argsStr, newIndex] = this._extractArguments(expression, parenIndex)
              // 递归解析参数
              const parsedArgs = this._parseArguments(argsStr).map((arg) => this._parseFunctions(arg.trim()))
              // 生成函数代码并添加到输出
              output += this.functions[funcName](parsedArgs)
              index = newIndex // 更新解析位置
              break
            }
          }
        }
      }

      if (!found) {
        // 未匹配到函数，直接复制字符
        output += expression[index]
        index++
      }
    }

    return output
  }

  /**
   * 提取函数参数（带括号匹配）
   * @private
   */
  _extractArguments(expression, startIndex) {
    let depth = 1
    let i = startIndex + 1
    const n = expression.length

    while (i < n && depth > 0) {
      switch (expression[i]) {
        case '(':
          depth++
          break
        case ')':
          depth--
          break
        case '"': // 跳过字符串内容
          i++
          while (i < n && expression[i] !== '"') {
            if (expression[i] === '\\') i++ // 处理转义字符
            i++
          }
          break
      }
      i++
    }

    if (depth !== 0) {
      throw new Error(`括号不匹配: ${expression.substring(startIndex)}`)
    }

    // 返回参数内容(不含外层括号)和结束位置
    return [expression.substring(startIndex + 1, i - 1), i]
  }

  /**
   * 解析函数参数
   * @private
   */
  _parseArguments(argsStr) {
    const args = []
    let current = ''
    let depth = 0

    for (let i = 0; i < argsStr.length; i++) {
      const char = argsStr[i]

      if (char === '(') depth++
      if (char === ')') depth--

      if (char === ',' && depth === 0) {
        args.push(current)
        current = ''
      } else {
        current += char
      }
    }

    if (current) args.push(current)
    return args
  }

  // 函数解析器实现 =================================================

  // 逻辑函数
  _parseIf(args) {
    return `(${args[0]} ? ${args[1]} : ${args[2]})`
  }

  _parseAnd(args) {
    return `(${args.join(' && ')})`
  }

  _parseOr(args) {
    return `(${args.join(' || ')})`
  }

  _parseNot(args) {
    return `(!${args[0]})`
  }

  _parseXor(args) {
    return `((${args[0]} && !${args[1]}) || (!${args[0]} && ${args[1]}))`
  }

  // 比较函数
  _parseEq(args) {
    return `(${args[0]} === ${args[1]})`
  }

  _parseNe(args) {
    return `(${args[0]} !== ${args[1]})`
  }

  _parseGt(args) {
    return `(${args[0]} > ${args[1]})`
  }

  _parseGe(args) {
    return `(${args[0]} >= ${args[1]})`
  }

  _parseLt(args) {
    return `(${args[0]} < ${args[1]})`
  }

  _parseLe(args) {
    return `(${args[0]} <= ${args[1]})`
  }

  // 数学函数
  _parseAdd(args) {
    return `(${args.join(' + ')})`
  }

  _parseSub(args) {
    return `(${args.join(' - ')})`
  }

  _parseMul(args) {
    return `(${args.join(' * ')})`
  }

  _parseDiv(args) {
    return `(${args[0]} / ${args[1]})`
  }

  _parseMod(args) {
    return `(${args[0]} % ${args[1]})`
  }

  _parsePow(args) {
    return `(Math.pow(${args[0]}, ${args[1]}))`
  }

  // 循环函数
  _parseSum(args) {
    if (args.length === 1) {
      // 当只有一个参数时，直接展开数组元素求和
      return `(Array.isArray(${args[0]}) ? 
            ${args[0]}.reduce((a, b) => a + b, 0) : 
            ${args[0]})`
    } else {
      // 多个参数时保持原逻辑
      return `([${args.join(',')}].reduce((a, b) => a + b, 0))`
    }
  }

  _parseProduct(args) {
    if (args.length === 1) {
      return `(Array.isArray(${args[0]}) ? 
            ${args[0]}.reduce((a, b) => a * b, 1) : 
            ${args[0]})`
    } else {
      return `([${args.join(',')}].reduce((a, b) => a * b, 1))`
    }
  }

  _parseCount(args) {
    return `([${args.join(',')}].length)`
  }

  _parseAverage(args) {
    if (args.length === 1) {
      return `(() => {
      const arr = Array.isArray(${args[0]}) ? 
                 ${args[0]} : 
                 [${args[0]}];
      return arr.reduce((a, b) => a + b, 0) / arr.length;
    })()`
    } else {
      return `(() => {
      const arr = [${args.join(',')}];
      return arr.reduce((a, b) => a + b, 0) / arr.length;
    })()`
    }
  }

  _parseMin(args) {
    return `(Math.min(...[${args.join(',')}]))`
  }

  _parseMax(args) {
    return `(Math.max(...[${args.join(',')}]))`
  }

  // 日期函数
  _parseDate(args) {
    return `(new Date(${args[0]}, ${args[1]}-1, ${args[2]}))`
  }

  // 修复后的日期差函数
  _parseDateDiff(args) {
    return `(() => {
      const parseDate = (val) => {
        if (val instanceof Date) return val;
        if (typeof val === 'string' || typeof val === 'number') return new Date(val);
        throw new Error('无效日期值: ' + val);
      };
      
      const start = parseDate(${args[0]});
      const end = parseDate(${args[1]});
      
      if (isNaN(start)) throw new Error('开始日期无效');
      if (isNaN(end)) throw new Error('结束日期无效');

      return Math.floor((end - start) / (1000 * 60 * 60 * 24));
    })()`
  }

  _parseDay(args) {
    return `(new Date(${args[0]}).getDate())`
  }

  _parseMonth(args) {
    return `(new Date(${args[0]}).getMonth() + 1)`
  }

  _parseYear(args) {
    return `(new Date(${args[0]}).getFullYear())`
  }

  // 文本函数
  _parseConcat(args) {
    return `([${args.join(',')}].join(''))`
  }

  _parseLen(args) {
    return `(String(${args[0]}).length)`
  }

  _parseLeft(args) {
    return `(String(${args[0]}).substring(0, ${args[1]}))`
  }

  _parseRight(args) {
    return `(() => { const str = String(${args[0]}); ` + `return str.substring(str.length - ${args[1]})) })()`
  }

  /**
   * 改进的MAP函数解析
   */
  _parseMap(args) {
    return `(${args[0]}.map(${args[1]}))`
  }

  /**
   * 改进的FILTER函数解析
   */
  _parseFilter(args) {
    // 支持两种调用方式:
    // 1. FILTER(数组, 条件表达式)
    // 2. FILTER(数组, (item) => 条件表达式)

    if (args.length !== 2) {
      throw new Error(`FILTER函数需要2个参数，但收到了${args.length}个`)
    }

    // 检查第二个参数是否是箭头函数形式
    if (args[1].includes('=>')) {
      // 直接使用提供的箭头函数
      return `(${args[0]}.filter(${args[1]}))`
    } else {
      // 自动包装为箭头函数
      return `(${args[0]}.filter(item => ${args[1]}))`
    }
  }

  _parseReduce(args) {
    return `([${args[0]}].reduce((${args[1]}, ${args[2]}) => ${args[3]}, ${args[4] || '0'}))`
  }

  /**
   * 改进的ARRAY函数解析
   */
  _parseArray(args) {
    if (args.length === 1) {
      // ARRAY(5) -> 创建长度为5的数组
      return `Array.from({length: ${args[0]}}, (_, i) => i)`
    } else if (args.length === 2 && args[1].includes('=>')) {
      // ARRAY(长度, mapper函数)
      return `Array.from({length: ${args[0]}}, ${args[1]})`
    } else if (args.length === 2) {
      // ARRAY(start, end)
      return `Array.from({length: ${args[1]} - ${args[0]} + 1}, (_, i) => ${args[0]} + i)`
    } else if (args.length === 3) {
      // ARRAY(start, end, step)
      return `(() => {
        const result = [];
        for (let i = ${args[0]}; i <= ${args[1]}; i += ${args[2]}) {
          result.push(i);
        }
        return result;
      })()`
    }
    throw new Error(`无效的ARRAY参数: ${args.join(', ')}`)
  }
}

const ExcelFields = ['input', 'number', 'textarea', 'date', 'select', 'computedays', 'amount']

/**
 * 判断对象中是否存在空值
 * @param {*} obj
 * @returns 返回布尔值
 */
function hasEmptyValue(obj) {
  // 遍历对象的所有值
  return Object.values(obj).some((value) => {
    // 检查值是否为 null 或 undefined
    if (value == null) return true

    // 检查值是否为字符串（包括空白字符串）
    if (typeof value === 'string') {
      return value.trim() === ''
    }
    return false
  })
}

/**
 * Excel公式计算服务
 */
class ExcelFormulaParserService {
  /**
   * 构造函数
   * @param {Object} config - 配置信息
   */
  constructor(config) {
    const copypublicVars = JSON.parse(JSON.stringify(config.publicVars))
    strTofunc(copypublicVars)
    this.parser = new ExcelFormulaParser(copypublicVars)
    this.formula = config.formula
  }

  /**
   * 计算并返回结果
   */
  parse(widgetList, formData) {
    if (Array.isArray(widgetList)) {
      const widget = widgetList
        .filter((x) => ExcelFields.includes(x.type))
        .map((x) => {
          return {
            label: 'DATA.' + x.options.label,
            value: x.options.name
          }
        })

      // 使用 reduce 方法转换数组
      const fieldVars = widget
        .filter((x) => this.formula.includes(x.label))
        .reduce((obj, item) => {
          obj[item.label] = formData[item.value] // 动态设置键值对
          return obj
        }, {})
      this.parser.setPublicVars(fieldVars)
      if (!hasEmptyValue(fieldVars)) {
        return this.parser.parse(this.formula)
      }
    }
    return ''
  }
}

/**
 * 函数类型字符串进行转换
 * 遍历 publicVars 对象的所有属性
 * @param {*} publicVars
 */
const strTofunc = (publicVars) => {
  for (const key in publicVars) {
    if (Object.prototype.hasOwnProperty.call(publicVars, key)) {
      const value = publicVars[key]
      if (typeof value === 'function') {
        continue
      }

      // 函数字符串
      if (typeof value === 'string') {
        const trimmedValue = value.trim()
        const isArrowFunction = trimmedValue.startsWith('(') && trimmedValue.includes('=>')
        const isTraditionalFunction = trimmedValue.startsWith('function') || trimmedValue.startsWith('async function')

        if (isArrowFunction || isTraditionalFunction) {
          try {
            publicVars[key] = new Function(`return (${trimmedValue})`)()
          } catch (e) {
            console.error(`函数转换失败 (${key}):`, e)
          }
        }
        continue
      }
    }
  }
}

/** 快速公式代码片段 */
const FastCodes = [
  /** 请假组件-退餐统计 */
  `SUM(
  MAP(
    FILTER(
      ARRAY(DATEDIFF({DATA.开始时间}, {DATA.结束时间}) + 1, 
        (_, i) => DATE(
          YEAR({DATA.开始时间}),
          MONTH({DATA.开始时间}),
          DAY({DATA.开始时间}) + i
        )
      ),
      date => {是否为工作日}(date)
    ),
    date => SUM(
      IF(
        {应退早餐}(date, new Date({DATA.开始时间}), new Date({DATA.结束时间}),{早餐时段}),
        {早餐价格}, 
        0
      ),
      IF(
        {应退午餐}(date, new Date({DATA.开始时间}), new Date({DATA.结束时间}),{午餐时段}),
        {午餐价格}, 
        0
      ),
      IF(
        {应退晚餐}(date, new Date({DATA.开始时间}), new Date({DATA.结束时间}),{晚餐时段}),
        {晚餐价格}, 
        0
      )
    )
  )
)`
]

/** 快速代码公共变量 */
const FastPublicVars = [
  {
    // 餐段配置
    早餐时段: ['7:00', '9:00'],
    午餐时段: ['11:00', '12:30'],
    晚餐时段: ['17:00', '19:00'],
    早餐价格: 5.0,
    午餐价格: 10.0,
    晚餐价格: 15.0,
    // 是否为工作日
    是否为工作日: `(date) => {
        const day = date.getDay()
        return day !== 0 && day !== 6 // 0=周日, 6=周六
      }`,
    // 餐段退款判断函数
    应退早餐: `(mealDate, startDate, endDate, period) => {
        // 解析餐段时间
        const parseTime = (time) => {
          if (typeof time === 'string') {
            const [hours, minutes] = time.split(':').map(Number)
            return { hours, minutes }
          }
          return time
        }
        const startTime = parseTime(period[0])
        const endTime = parseTime(period[1])

        // 早餐时段开始时间
        const mealStart = new Date(mealDate)
        mealStart.setHours(startTime.hours, startTime.minutes, 0, 0)

        // 早餐时段结束时间
        const mealEnd = new Date(mealDate)
        mealEnd.setHours(endTime.hours, endTime.minutes, 0, 0)

        // 检查是否为同一天
        const isSameDay = (d1, d2) =>
          d1.getFullYear() === d2.getFullYear() &&
          d1.getMonth() === d2.getMonth() &&
          d1.getDate() === d2.getDate()

        // 如果是开始日，检查请假开始时间是否在早餐时段之后
        if (isSameDay(mealDate, startDate)) {
          return startDate <= mealStart
        }

        // 如果是结束日，检查请假结束时间是否在早餐时段之前
        if (isSameDay(mealDate, endDate)) {
          return endDate >= mealEnd
        }

        // 中间日全额退款
        return true
      }`,

    应退午餐: `(mealDate, startDate, endDate, period) => {
        const parseTime = (time) => {
          if (typeof time === 'string') {
            const [hours, minutes] = time.split(':').map(Number)
            return { hours, minutes }
          }
          return time
        }

        const startTime = parseTime(period[0])
        const endTime = parseTime(period[1])

        const mealStart = new Date(mealDate)
        mealStart.setHours(startTime.hours, startTime.minutes, 0, 0)

        const mealEnd = new Date(mealDate)
        mealEnd.setHours(endTime.hours, endTime.minutes, 0, 0)

        const isSameDay = (d1, d2) =>
          d1.getFullYear() === d2.getFullYear() &&
          d1.getMonth() === d2.getMonth() &&
          d1.getDate() === d2.getDate()

        if (isSameDay(mealDate, startDate)) {
          return startDate <= mealStart
        }

        if (isSameDay(mealDate, endDate)) {
          return endDate >= mealEnd
        }

        return true
      }`,

    应退晚餐: `(mealDate, startDate, endDate, period) => {
        const parseTime = (time) => {
          if (typeof time === 'string') {
            const [hours, minutes] = time.split(':').map(Number)
            return { hours, minutes }
          }
          return time
        }

        const startTime = parseTime(period[0])
        const endTime = parseTime(period[1])

        const mealStart = new Date(mealDate)
        mealStart.setHours(startTime.hours, startTime.minutes, 0, 0)

        const mealEnd = new Date(mealDate)
        mealEnd.setHours(endTime.hours, endTime.minutes, 0, 0)

        const isSameDay = (d1, d2) =>
          d1.getFullYear() === d2.getFullYear() &&
          d1.getMonth() === d2.getMonth() &&
          d1.getDate() === d2.getDate()

        if (isSameDay(mealDate, startDate)) {
          return startDate <= mealStart
        }

        if (isSameDay(mealDate, endDate)) {
          return endDate >= mealEnd
        }

        return true
      }`
  }
]

export { ExcelFormulaParserService, ExcelFields, FastCodes, FastPublicVars, strTofunc }
